package com.jimmyhsu.ecnudaowei.Utils;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.ImageView;

import com.jimmyhsu.ecnudaowei.Bean.TaskActivity;

import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

//import org.apache.http.Header;
//import org.apache.http.HttpResponse;
//import org.apache.http.NameValuePair;
//import org.apache.http.client.entity.UrlEncodedFormEntity;
//import org.apache.http.client.methods.HttpGet;
//import org.apache.http.client.methods.HttpPost;
//import org.apache.http.client.params.ClientPNames;
//import org.apache.http.impl.client.DefaultHttpClient;
//import org.apache.http.message.BasicNameValuePair;

/**
 * Created by jimmyhsu on 2017/2/19.
 */

public class EcnuUtils {

    private boolean verbose = true;

    private static final String TAG = "EcnuUtils";
    private static final String EAMS_URL = "https://portal1.ecnu.edu.cn" +
            "/cas/login?service=http%3A%2F%2Fapplicationnewjw.ecnu.edu.cn%2Feams%2Fhome.action";
    private static final String USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3)" +
            " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36";
    private static final String DEFAULT_USERNAME = "未知用户";

    private Handler mHandler;
    private boolean isPrepared = false;
    private ExecutorService mThreadPool = Executors.newCachedThreadPool();

    private String mFirstSessionId;
    private Document mFirstDocument;
    private Map<String, String> mCookies;
    private boolean isVerified = false;

    private ImageView mCaptchaView;

    public enum USER_TYPE {
        UNDERGRADUATE, STAFF, POSTGRADUATE, UNKNOWN
    }

    public EcnuUtils() {
        mHandler = new Handler(Looper.getMainLooper());
    }


    /**
     * 将cookie放入map中
     * @param generalCookies
     * @param loginResponse
     */
    private void refreshCookies(Map<String, String> generalCookies, Connection.Response loginResponse) {
        Map<String, String> cookie = loginResponse.cookies();
        for (String key : cookie.keySet()) {
            generalCookies.put(key, cookie.get(key));
        }
    }

    /**
     * 下载图片
     * @param url
     * @param cookies 可能会需要cookie
     * @return
     */
    private Bitmap downloadImage(String url, Map<String, String> cookies) {
        try {
            Connection connect = Jsoup.connect(url);
            if (cookies != null && !cookies.isEmpty()) {
                connect.cookies(cookies);
            }
            Connection.Response response = connect.execute();
            byte[] img = response.bodyAsBytes();
            return BitmapFactory.decodeByteArray(img, 0, img.length);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将下载的Bitmap放入ImageView
     * @param captchaAddress
     * @param cookies
     * @param captchaView
     */
    private void downloadCaptcha(String captchaAddress, Map<String, String> cookies, final ImageView captchaView) {
        final Bitmap captchBm = downloadImage(captchaAddress, cookies);
        captchaView.post(new Runnable() {
            @Override
            public void run() {
                captchaView.setImageBitmap(captchBm);

            }
        });
    }


    /**
     * 入口方法，需要实名验证或课程表爬取前都需要调用，在主线程调用,模拟打开登录页面
     * @param captchaView 存放验证码的ImageView
     */
    public void prepare(final ImageView captchaView) {

        mCaptchaView = captchaView;
        reset();
        mThreadPool.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    mCookies = new HashMap<>();
                    Connection eamsHomeConnection = Jsoup.connect(EAMS_URL);
                    handleConnection(eamsHomeConnection, false);
                    Connection.Response eamsHomeResponse = eamsHomeConnection.execute();
                    refreshCookies(mCookies, eamsHomeResponse);
                    mFirstSessionId = eamsHomeResponse.cookie("JSESSIONID");
                    mFirstDocument = Jsoup.parse(eamsHomeResponse.body());//转换为Dom树
                    downloadCaptcha("https://portal1.ecnu.edu.cn/cas/Captcha.jpg?" + Math.random()
                            , mCookies, captchaView);
                    isPrepared = true;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

    }

    private void reset() {
        mFirstSessionId = null;
        mFirstDocument = null;
        isVerified = false;
        if (mCookies != null) {
            mCookies.clear();
            mCookies = null;
        }
    }


    /**
     * 验证实名，主线程
     * @param studentId
     * @param password
     * @param captcha
     * @param listner
     */
    public void verify(final String studentId, final String password, final String captcha,
                       final OnVerifyListner listner) {
        if (!isPrepared || isVerified) {
            throw new IllegalStateException("Haven't prepared or prepare failed " +
                    "(probably due to network error)! Invoke #prepare first then #verify");
        }

        mThreadPool.submit(new Runnable() {
            @Override
            public void run() {
                checkUser(studentId, password, captcha, listner);
            }
        });
    }

    private void checkUser(String studentId, String password, String captcha, final OnVerifyListner listner) {
        USER_TYPE resultType = USER_TYPE.UNKNOWN;
        String name = DEFAULT_USERNAME;
        Bitmap avatarBm = null;
        boolean isValid = true;
        try {
            List<Element> et = mFirstDocument.select("form[name=login_form]");//获取form表单


            //获取，cookie和表单属性，下面map存放post时的数据
            Map<String, String> datas = new HashMap<>();
            for (Element e : et.get(0).getAllElements()) {
                if (e.attr("name").equals("username")) {
                    e.attr("value", studentId);//设置用户名
                }

                if (e.attr("name").equals("password")) {
                    e.attr("value", password); //设置用户密码
                }

                if (e.attr("name").equals("captcha")) {
                    e.attr("value", captcha); //设置验证码
                }

                if (e.attr("name").length() > 0) {//排除空值表单属性
                    datas.put(e.attr("name"), e.attr("value"));
                }
            }
            datas.put("service", "http://applicationnewjw.ecnu.edu.cn/eams/home.action");
            Connection submitFormConn = Jsoup.connect("https://portal1.ecnu.edu.cn/cas/login");
            handleConnection(submitFormConn, false);
            //设置cookie和post上面的map数据
            Connection.Response loginResponse = submitFormConn.ignoreContentType(true)
                    .method(Connection.Method.POST)
                    .data(datas)
                    .cookies(mCookies)
                    .execute();

            refreshCookies(mCookies, loginResponse);
            String CasService = loginResponse.header("CAS-Service");
            String ticket = loginResponse.header("CAS-Ticket");
            if (ticket == null) {
                isValid = false;
            }
            if (verbose) {
                Log.e("Ecnu", "ticket = " + ticket);
            }
            if (isValid) {
                Connection eamsConn = Jsoup.connect(CasService + "?ticket=" + ticket);
                handleConnection(eamsConn, true);
                Connection.Response eamsResponse = eamsConn.execute();
//            log(eamsResponse.body());
                mFirstSessionId = eamsResponse.cookie("JSESSIONID");
//            log("sessionid: " + firstSessionId);
                Document eamsDoc = Jsoup.parse(eamsResponse.body());
                Elements forms = eamsDoc.select("form");
                Elements imgs = eamsDoc.select("img[width=45px]");
                if (!forms.isEmpty()) {
                    Element form = forms.get(0);
                    Element font = form.select("font").get(0);
                    Elements spans = form.select(".f4");
                    resultType = getUserType(spans.get(1).html());
                    name = font.html().substring(0, font.html().indexOf("("));
//                log(font.html());
//                log("类型:" + spans.get(1).html());
//                log("职业:" + spans.get(2).html());
                }
                if (!imgs.isEmpty()) {
                    Element avatar = imgs.get(0);
                    String avatarUrl = "http://applicationnewjw.ecnu.edu.cn" + avatar.attr("src");
                    avatarBm = downloadAvatar(avatarUrl, CasService + "?ticket=" + ticket);
                }
            }
            if (listner != null) {
                final String finalName = name;
                final USER_TYPE finalResultType = resultType;
                final Bitmap finalAvatarBm = avatarBm;
                final boolean finalIsValid = isValid;
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        listner.onVerify(finalIsValid, finalName, finalResultType, finalAvatarBm);
                    }
                });
            }
            if (isValid) isPrepared = false;
            else {
                prepare(mCaptchaView);
            }

        } catch (Exception e) {
            e.printStackTrace();
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    if (listner != null) {
                        listner.onVerify(false, null, null, null);
                    }
                }
            });
        }
    }

    public void refreshCaptcha(ImageView captchaView) {
        if (mCookies == null || captchaView == null) return;
        downloadCaptcha("https://portal1.ecnu.edu.cn/cas/Captcha.jpg?" + Math.random()
                , mCookies, captchaView);
    }

    public void refreshCaptcha() {
        if (mCookies == null || mCaptchaView == null) return;
        downloadCaptcha("https://portal1.ecnu.edu.cn/cas/Captcha.jpg?" + Math.random()
                , mCookies, mCaptchaView);
    }

    private USER_TYPE getUserType(String typeString) {
        if (typeString.contains("本科")) {
            return USER_TYPE.UNDERGRADUATE;
        } else if (typeString.contains("研究")) {
            return USER_TYPE.POSTGRADUATE;
        } else if (typeString.contains("教师")) {
            return USER_TYPE.STAFF;
        } else {
            return USER_TYPE.UNKNOWN;
        }
    }


    private Bitmap downloadAvatar(String avatarUrl, String referer) {

        try {
            Connection avatarConn = Jsoup.connect(avatarUrl)
                    .ignoreContentType(true)
                    .header("Cookie", "JSESSIONID=" + mFirstSessionId)
                    .header("Referer", referer);
            handleConnection(avatarConn, true);
            Connection.Response response = avatarConn.execute();
            byte[] img = response.bodyAsBytes();
            return BitmapFactory.decodeByteArray(img, 0, img.length);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }


    }



    private void handleConnection(Connection conn, boolean followRedirect) {
        conn.followRedirects(followRedirect);
        conn.header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
        conn.header("Accept-Encoding", "gzip, deflate, sdch");
        conn.header("Accept-Language", "zh-CN,zh;q=0.8,en;q=0.6");
        conn.header("User-Agent", USER_AGENT);
    }

    public void stop() {
        mThreadPool.shutdownNow();
    }

    private List<TaskActivity> mCourseTable = new ArrayList<>();
    private TaskActivity mTempTask;

    /**
     * 入口方法，获取课程表,主线程调用
     * @param studentId
     * @param password
     * @param captchaAnswer
     * @param listner
     */
    public void getCourseTable(final String studentId, final String password
            , final String captchaAnswer, final OnGetCourseListner listner) {
        if (!isPrepared) throw new IllegalStateException("Wrong state: should invoke #prepare first");

        mThreadPool.submit(new Runnable() {
            @Override
            public void run() {
                doGetCourseInfo(studentId, password, captchaAnswer, listner);
            }
        });
    }

    private void doGetCourseInfo(String studentId, String password, String captchaAnswer
            , OnGetCourseListner listner) {
//        Log.e("EcnuUtils", "stuId = " + studentId + ", password = " + password);
        try {
            List<Element> et = mFirstDocument.select("form[name=login_form]");

            Map<String, String> datas = new HashMap<>();
            for (Element e : et.get(0).getAllElements()) {
                if (e.attr("name").equals("username")) {
                    e.attr("value", studentId);//用户名
                }

                if (e.attr("name").equals("password")) {
                    e.attr("value", password); //用户密码
                }

                if (e.attr("name").equals("captcha")) {
                    e.attr("value", captchaAnswer); //验证码
                }

                if (e.attr("name").length() > 0) {//其他表单属性
                    datas.put(e.attr("name"), e.attr("value"));
                }
            }
            for (String key : mCookies.keySet()) {
                Log.e(key, mCookies.get(key));
            }
            datas.put("service", "http://applicationnewjw.ecnu.edu.cn/eams/home.action");
            Connection submitFormConn = Jsoup.connect("https://portal1.ecnu.edu.cn/cas/login");
            handleConnection(submitFormConn, false);
            //设置cookie和post上面的map数据
            Connection.Response loginResponse = submitFormConn.ignoreContentType(true)
                    .method(Connection.Method.POST)
                    .data(datas)
                    .cookies(mCookies)
                    .execute();

            refreshCookies(mCookies, loginResponse);
            String CasService = loginResponse.header("CAS-Service");
            String ticket = loginResponse.header("CAS-Ticket");

            Connection eamsConn = Jsoup.connect(CasService + "?ticket=" + ticket);
            handleConnection(eamsConn, true);
            Connection.Response eamsResponse = eamsConn.execute();
            refreshCookies(mCookies, eamsResponse);

            String myInfoUrl = "http://applicationnewjw.ecnu.edu.cn/eams/courseTableForStd!courseTable.action";
            Connection myInfoConn = Jsoup.connect(myInfoUrl)
                    .cookies(mCookies);
            handleConnection(myInfoConn, false);
            Map<String, String> data = new HashMap<>();
            data.put("ignoreHead","1");//ignoreHead:1
            data.put("setting.kind", "std");//setting.kind:std
            data.put("startWeek", "1");//startWeek:1
            data.put("semester.id", "577");//semester.id:577
            data.put("ids", "346884");//ids:347330 pf:347213
            Connection.Response myInfoResp = myInfoConn.method(Connection.Method.POST)
                    .data(data)
                    .execute();
            Document courseDoc = Jsoup.parse(myInfoResp.body());
//            log(myInfoResp.body());
            Elements scripts = courseDoc.select("script[language=JavaScript]");
            if (scripts.size() < 3) {
                log("web page error!");
                if (listner != null) {
                    listner.onCourseGet(false, null);
                }
            } else {
                Element targetScript = scripts.get(2);
                parseScript(targetScript);
                if (listner != null) {
                    listner.onCourseGet(true, mCourseTable);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            if (listner != null) {
                listner.onCourseGet(false, null);
            }
        }
    }

    private enum JS_TYPE {
        IRRELEVANT, DEFINE_COURSE, DEFINE_TIME
    }



    private void parseScript(Element targetScript) {
        String script = targetScript.html();
//        log(script);
        String[] jsExprs = script.split(";");
        JS_TYPE type;
        for (String js : jsExprs) {
            js = trim(js);
            if (js.startsWith("activity")) {
                type = JS_TYPE.DEFINE_COURSE;
            } else if (js.startsWith("index")) {
                type = JS_TYPE.DEFINE_TIME;
            } else {
                type = JS_TYPE.IRRELEVANT;
            }
            if (type != JS_TYPE.IRRELEVANT) {
                handleDefinition(js, type);
            }
        }
    }

    public static String trim(String str) {
        String dest = "";
        if (str!=null) {
            Pattern p = Pattern.compile("\\s*|\t|\r|\n");
            Matcher m = p.matcher(str);
            dest = m.replaceAll("");
        }
        return dest;
    }

    private void handleDefinition(String js, JS_TYPE type) {
        removeQuotationMarks(js);
        if (type == JS_TYPE.DEFINE_COURSE) {
            //activity = new TaskActivity("76694","赵慧","302239(SOFT0031131071.02)","数据库系统及应用实践(SOFT0031131071.02)","2609","理科大楼B226","01010101010101010101000000000000000000000000000000000")
            String[] info = js.split(",");
            String teacher = removeQuotationMarks(info[1]);
            String courseName = removeQuotationMarks(info[3].substring(0, info[3].indexOf("(")));
            String courseId = removeQuotationMarks(info[3].substring(info[3].indexOf("(")+1, info[3].length() - 2));
            String location = removeQuotationMarks(info[5]);
            String week = removeQuotationMarks(info[6].substring(0, info[6].length()-1));
            mTempTask = new TaskActivity(courseId, courseName, teacher, location, week, -1, -1, false);
        } else if (type == JS_TYPE.DEFINE_TIME && mTempTask != null) {
            //index =3*unitCount+2;
            insertIntoCourseTable(Integer.parseInt(js.charAt(js.indexOf("*")-1)+"")
                    , Integer.parseInt(js.substring(js.length()-1)));
        }
    }


    private static String removeQuotationMarks(String src) {
        return src.replaceAll("\"", "");
    }

    private void insertIntoCourseTable(int weekday, int classNumber) {
        if (mTempTask.getDay() < 0) {
            mTempTask.setDay(weekday);
        }
        if (mTempTask.getNumber() < 0) {
            mTempTask.setNumber(classNumber);
        }
        if (mCourseTable.size() > 0) {
            TaskActivity lastCourse = mCourseTable.get(mCourseTable.size() - 1);
            if (lastCourse.isAdjacent(mTempTask, weekday, classNumber)) {
                lastCourse.setDuration(lastCourse.getDuration()+1);
            } else {
                mCourseTable.add(mTempTask);
            }
        } else {
            mCourseTable.add(mTempTask);
        }
    }


//    /**
//     * 如果只需要检查用户学号和密码是否存在且匹配，调用此方法
//     *
//     * @param studentId 学号
//     * @param password  公共数据库密码
//     * @return 是否存在且匹配
//     */
//    public void checkValiditySimple(final String studentId, final String password, final OnVerifyListner listener) {
//        if (listener == null) {
//            throw new IllegalArgumentException("OnVerifyListner should not be null");
//        }
//        mThreadPool.submit(new Runnable() {
//            @Override
//            public void run() {
//                checkEcnuAcount(studentId, password, listener);
//            }
//        });
//    }
//    private void checkEcnuAcount(String studentId, String password, OnVerifyListner listener) {
//        String loginUrl = "https://docs.ecnu.edu.cn/vpn";
//        String jSessionId = "";
//        List<NameValuePair> parmasList = initLoginParmas(studentId, password);
//        HttpGet get = new HttpGet(loginUrl);
//        get.getParams().setParameter(ClientPNames.HANDLE_REDIRECTS, false);
//        get.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" +
//                " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36");
//        try {
//            HttpResponse response = new DefaultHttpClient().execute(get);
//            String location = response.getFirstHeader("Location").getValue();
//
//            HttpGet get2 = new HttpGet(location);
//            get2.getParams().setParameter(ClientPNames.HANDLE_REDIRECTS, false);
//            HttpResponse response2 = new DefaultHttpClient().execute(get2);
//
//            String lc = "";
//            Header[] headers2 = response2.getHeaders("Set-Cookie");
//            for (Header header : headers2) {
//                String[] values = header.getValue().split(";");
//                for (String value : values) {
//                    if (value.contains("JSESSIONID")) {
//                        jSessionId = value.substring(value.indexOf("=") + 1);
//                    } else if (value.contains("_idp_authn_lc_key")) {
//                        lc = value.substring(value.indexOf("=") + 1);
//                    }
//                }
//            }
//            StringBuilder cookie = new StringBuilder();
//            cookie.append("JSESSIONID=").append(jSessionId)
//                    .append(";_idp_authn_lc_key=").append(lc);
//
//
//            HttpPost post = new HttpPost("https://docs.ecnu.edu.cn/idp/Authn/UserPassword");
//            post.getParams().setParameter(ClientPNames.HANDLE_REDIRECTS, false);
//            post.setHeader("User-Agent",
//                    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)" +
//                            " Chrome/56.0.2924.87 Safari/537.36");
//            post.setHeader("Content-Type",
//                    "application/x-www-form-urlencoded;charset=gbk");
//            post.setHeader("Cookie", cookie.toString());
//            post.setEntity(new UrlEncodedFormEntity(parmasList, "GBK"));
//            HttpResponse response5 = new DefaultHttpClient().execute(post);
//            listener.onVerify(response5.getStatusLine().getStatusCode() == 302,
//                    null, null, null);
//        } catch (IOException e) {
//            e.printStackTrace();
//            listener.onVerify(false, null, null, null);
//        }
//    }
//
//    private List<NameValuePair> initLoginParmas(String username, String password) {
//        List<NameValuePair> paramsList = new ArrayList<>();
//        HashMap<String, String> params = new HashMap<>();
//        params.put("j_username", username);
//        params.put("j_password", password);
//        Set<String> keySet2 = params.keySet();
//        for (String temp : keySet2) {
//            paramsList.add(new BasicNameValuePair(temp, params.get(temp)));
//        }
//        return paramsList;
//    }

    private void log(String content) {
        Log.e(TAG, content);
    }

    public interface OnVerifyListner {
        void onVerify(boolean isValid, String studentName, USER_TYPE type, Bitmap avatar);
    }

    public interface OnGetCourseListner {
        void onCourseGet(boolean success, List<TaskActivity> courseTable);
    }
}
